#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include"log.hpp"
using namespace std;
Log log;//产生一个日志对象

//学习共享内存：
//就是创建一片内存映射在共享区，这样不同的进程可以访问同一片内存
//共享内存是所有进程间通信最快的
//共享内存没有同步互斥之类的保护机制，可以通过管道类有保护机制的通信方式辅助使用
//共享内存大小建议是4096的整数倍，就算我们申请4097，其实操作系统给我们的是4096*2大小的空间，但是我们只能访问前4097个字节的空间
const int size=4096;
const string pathname="/home/hxk";
const int proj_id=0x6666;


//计算出key值,ftok 用于从一个路径名和一个项目标识符生成一个唯一的键。这个键可以用来标识共享内存段、消息队列或信号量。
key_t GetKey()
{
    key_t k=ftok(pathname.c_str(),proj_id);
    //是一套算法，如果输入路径名和项目标识符一样的话就会生成一样的key值，不同进程拿着同一个k值就可以访问同一块共享内存

    if(k<0)
    {
        log(Fatal,"ftok error:%s",strerror(errno));
        exit(1);
    }
    log(Info,"ftok success,key is:0x%x",k);
    return k;

}
//标志位：
// IPC_CREAT: 如果指定的共享内存段或消息队列不存在，则创建一个新的。
// IPC_EXCL: 如果共享内存段或消息队列已经存在，调用会失败（返回 -1），这样可以避免意外覆盖已存在的对象。
// 0666: 这是一个权限掩码，用于设置创建的共享内存或消息队列的权限。在这个例子中，0666 表示所有用户都有读写权限。
int GetShareMemHelper(int flag)//标志位传参
{
    key_t k=GetKey();//获取一个唯一的key值用来作为访问这块共享内存的钥匙
    int shmid=shmget(k,size,flag);//系统调用，获取一块内存空间，返回这块空间的id
    //flag是标志位传参，设置这块内存的使用权限
    if(shmid<0)
    {
        log(Fatal,"create share memory error:%s",strerror(errno));
        exit(2);
    }
    log(Info,"create share memory success,shmid:%d",shmid);
    
    return shmid;

}

//创建shm：
int CreateShm()
{
    return GetShareMemHelper(IPC_CREAT|IPC_EXCL|0666);

}
//获取共享内存：
int GetShm()
{
    return GetShareMemHelper(IPC_CREAT);
}

//创建命名管道：
#define FIFO_FILE "./myfifo"//定义好路径和文件名作为管道文件
#define MODE 0666//权限模式
enum
{
    FIFO_CREATE_ERR=1,//第一个是1，后面的枚举类型就是递增的数字
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
};

class Init
{
public:
    Init()
    {
        //创建命名管道
        int n=mkfifo(FIFO_FILE,MODE);//在指定的字符串类型的路径中创建权限为MODE的管道
        if(n==-1)
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }

    }
    ~Init()//管道的析构
    {
        //虽然系统会在进程结束时自动清理未关闭的 FIFO 文件，但显式删除是更好的实践。
        int m=unlink(FIFO_FILE);//使用unlink可以手动删除命名管道,指定管道的路径即可
        if(m==-1)
        {
            perror("unlink");
            exit(FIFO_DELETE_ERR);
        }
    }

};